package com.lq.dubbo.rocketmq.shop.service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.lq.dubbo.rocketmq.shop.api.ICouponService;
import com.lq.dubbo.rocketmq.shop.api.IGoodsService;
import com.lq.dubbo.rocketmq.shop.api.IOrderService;
import com.lq.dubbo.rocketmq.shop.api.IUserService;
import com.lq.dubbo.rocketmq.shop.constant.ShopCode;
import com.lq.dubbo.rocketmq.shop.entity.MQEntity;
import com.lq.dubbo.rocketmq.shop.entity.Result;
import com.lq.dubbo.rocketmq.shop.exception.CastException;
import com.lq.dubbo.rocketmq.shop.mapper.TradeOrderMapper;
import com.lq.dubbo.rocketmq.shop.shop_pojo.*;
import com.lq.dubbo.rocketmq.shop.util.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;

/**
 * @program: dubborocketmqshop
 * @description:
 * @author: LQ
 * @create: 2021-06-04 15:53
 */
@Component
@Slf4j
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
    @Reference
    private IGoodsService goodsService;
    @Reference
    private IUserService userService;
    @Reference
    private ICouponService couponService;
    @Autowired
    private TradeOrderMapper orderMapper;

    /**
     * rocketMQ模板类
     */
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    IdWorker idWorker;

    @Value("${mq.order.topic}")
    private String topic;
    @Value("${mq.order.tag.cancel}")
    private String tag;

    /**
     * 下单接口
     * 1需要校验订单
     * 2生成订单
     * 3扣减库存
     * 4扣减优惠券
     * 5使用余额
     * 6返回成功状态
     * 如果3 4 5 6中存在失败,
     * 1 发送消息确认订单失败
     * 2 返回失败状态
     *
     * @param order 订单对象
     * @return
     */
    @Override
    public Result confirmOrder(TradeOrder order) {
        // 1.校验订单
        checkOrder(order);
        // 2生成预订单(保存)
        savePreOrder(order);
        try {
            // 3扣减库存
            reduceGoodsNum(order);
            // 4扣减优惠券
            updateCouponStatus(order);
            // 5使用余额
            reduceMoneyPaid(order);

            // 模拟异常
            CastException.cast(ShopCode.SHOP_FAIL);

            // 6确认订单(将订单设置为可见)
            updateOrderStatus(order);
            // 7返回成功状态
            return new Result(ShopCode.SHOP_SUCCESS.getSuccess(), ShopCode.SHOP_SUCCESS.getMessage());
        } catch (Exception e) {
            MQEntity mqEntity = new MQEntity();
            mqEntity.setOrderId(order.getOrderId());
            mqEntity.setUserId(order.getUserId());
            mqEntity.setUserMoney(order.getMoneyPaid());
            mqEntity.setGoodsId(order.getGoodsId());
            mqEntity.setGoodsNum(order.getGoodsNumber());
            mqEntity.setCouponId(order.getCouponId());
            e.printStackTrace();
            // 1 发送消息确认订单失败
            try {
                sendCancelOrder(topic, tag, order.getOrderId().toString(), JSON.toJSONString(mqEntity));
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            // 2 返回失败状态
            return new Result(ShopCode.SHOP_FAIL.getSuccess(), ShopCode.SHOP_SUCCESS.getMessage());
        }
    }

    /**
     * 发送订单发送失败的消息
     *
     * @param topic 主体
     * @param tag   tag
     * @param keys  key
     * @param body  消息体
     */
    private void sendCancelOrder(String topic, String tag, String keys, String body) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
        Message message = new Message(topic, tag, keys, body.getBytes());
        rocketMQTemplate.getProducer().send(message);

    }

    /**
     * 确认订单
     *
     * @param order
     */
    private void updateOrderStatus(TradeOrder order) {
        order.setOrderStatus(ShopCode.SHOP_ORDER_CONFIRM.getCode());
        order.setPayStatus(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY.getCode());
        order.setConfirmTime(new Date());
        int r = orderMapper.updateByPrimaryKey(order);
        if (r <= 0) {
            CastException.cast(ShopCode.SHOP_ORDER_COMFIRM_FAIL);
        }
        log.info("订单:" + order.getOrderId() + "确认订单成功");
    }

    /**
     * 扣余额
     *
     * @param order
     */
    private void reduceMoneyPaid(TradeOrder order) {
        if (order.getMoneyPaid() != null && order.getMoneyPaid().compareTo(BigDecimal.ZERO) > 0) {
            TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
            userMoneyLog.setOrderId(order.getOrderId());
            userMoneyLog.setUserId(order.getUserId());
            userMoneyLog.setUseMoney(order.getMoneyPaid());
            userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_PAID.getCode());
            userMoneyLog.setCreateTime(new Date());
            Result result = userService.updateMoneyPaid(userMoneyLog);
            if (result.getSuccess().equals(ShopCode.SHOP_FAIL.getCode())) {
                CastException.cast(ShopCode.SHOP_USER_MONEY_REDUCE_FAIL);
            }
            log.info("订单:" + order.getOrderId() + ",扣减余额成功");
        }
    }

    private void updateCouponStatus(TradeOrder order) {
        if (order.getCouponId() != null) {
            TradeCoupon coupon = couponService.findOne(order.getCouponId());
            coupon.setOrderId(order.getOrderId());
            coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
            coupon.setUsedTime(new Date());
            // 更新优惠券状态
            Result result = couponService.updateCouponStatus(coupon);
            if (result.getSuccess().equals(ShopCode.SHOP_FAIL.getCode())) {
                CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
            }
            log.info("订单:" + coupon.getOrderId() + ",使用优惠券");
        }
    }

    /**
     * 扣减库存
     *
     * @param order
     */
    private void reduceGoodsNum(TradeOrder order) {
        // 订单id,商品id,商品数量
        TradeGoodsNumbersLogs goodsNumbersLogs = new TradeGoodsNumbersLogs();
        goodsNumbersLogs.setOrderId(order.getOrderId());
        goodsNumbersLogs.setGoodsId(order.getGoodsId());
        goodsNumbersLogs.setGoodsNumber(order.getGoodsNumber());
        Result result = goodsService.reduceGoodsNum(goodsNumbersLogs);
        if (result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())) {
            CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
        }
        log.info("订单:" + order.getOrderId() + "扣减库存成功");
    }

    /**
     * 校验订单
     *
     * @param order
     */
    private void checkOrder(TradeOrder order) {
        // 1. 校验订单是否存在
        if (order == null) {
            CastException.cast(ShopCode.SHOP_ORDER_INVALID);
        }
        // 2. 校验商品是否存在
        Long goodsId = order.getGoodsId();
        TradeGoods goods = goodsService.findOne(goodsId);
        if (goods == null || goods.getGoodsId() == null) {
            CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
        }
        // 3. 校验用户是否存在
        TradeUser user = userService.findOne(order.getUserId());
        if (user == null || user.getUserId() == null) {
            CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
        }
        // 4. 校验商品单件是否合法(订单金额是否等于商品价格乘以商品数量)好像没有考虑一个订单多个商品的情况....
        // BigDecimal运算比较+-*/ add(+)subtract(-)multiply(*)divide(/)compareTo(比较大小,返回int(0相等 -1小于1大于))
        if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
            CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
        }
        // 5. 校验商品数量是否合法
        if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
            CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
        }
        log.info("校验订单通过");
    }

    /**
     * 生成预订单
     * 将订单保存到数据库中,但是用户不可见
     *
     * @param order
     * @return
     */
    private Long savePreOrder(TradeOrder order) {
        // 1设置订单状态为不可见
        order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
        // 2设置订单id
        long orderId = idWorker.nextId();
        order.setOrderId(orderId);
        // 3核算订单运费(如果订单金额大于100,免运费,小于100,10元运费)
        BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
        if (shippingFee.compareTo(order.getShippingFee()) != 0) {
            CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
        }
        // 4核算订单总金额是否合法
        BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
        orderAmount.add(shippingFee);
        if (order.getOrderAmount().compareTo(orderAmount) != 0) {
            CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
        }
        // 5判断用户是否使用余额
        BigDecimal moneyPaid = order.getMoneyPaid();
        if (moneyPaid != null) {
            int r = moneyPaid.compareTo(BigDecimal.ZERO);
            // 5.1余额小于0
            if (r < 0) {
                CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
            }
            // 5.2余额大于0
            if (r > 0) {
                TradeUser user = userService.findOne(order.getUserId());
                if (moneyPaid.compareTo(user.getUserMoney()) == 0) {
                    CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
                }
            }
        } else {
            order.setMoneyPaid(BigDecimal.ZERO);
        }
        // 6判断用户是否使用优惠券
        Long couponId = order.getCouponId();
        if (couponId != null) {
            TradeCoupon coupon = couponService.findOne(couponId);
            // 6.1判断优惠券是否存在
            if (coupon == null) {
                CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
            }
            // 6.2判断优惠券是否使用
            if (coupon.getIsUsed().intValue() == ShopCode.SHOP_COUPON_ISUSED.getCode().intValue()) {
                CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
            }
            order.setCouponPaid(coupon.getCouponPrice());
        } else {
            order.setCouponPaid(BigDecimal.ZERO);
        }
        // 7核算订单支付金额 订单总金额-余额-优惠金额
        BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
        order.setPayAmount(payAmount);
        // 8设置下单时间
        order.setAddTime(new Date());
        // 9保存订单到数据库
        int insert = orderMapper.insert(order);
        // 10返回订单id
        return orderId;
    }

    /**
     * 计算运费
     *
     * @param orderAmount 订单金额
     * @return 运费
     */
    private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
        // 大于100,等于100也有
        if (orderAmount.compareTo(new BigDecimal(100)) == 1) {
            return BigDecimal.ZERO;
        }
        // 等于100的时候,也有10的运费
        return new BigDecimal(10);
    }
}
